package metaphor.persistence.filter;

import java.util.ArrayList;
import java.util.List;
import java.util.Date;

/**
 * Query
 * @author Mark Ashworth <javameme@gmail.com>
 * @version 1.0.0
 */
public class Query {
	private Class persistentClass;
	private List<Expression> criteria;
    private List<Order> order;
    private List<Association> associations;

	public Query(Class persistentClass) {
		this.persistentClass = persistentClass;
	}
	
	public Class getPersistentClass() {
		return this.persistentClass;
	}
	
	public void setPersistentClass(Class queryClass) {
		if (queryClass == null) throw new IllegalArgumentException("The query class may not be null");
		this.persistentClass = queryClass;
	}
	
	public List<Expression> getCriteria() {
        if (this.criteria == null) {
            this.criteria = new ArrayList<Expression>();
        }
		return this.criteria;
	}

    public List<Order> getOrder() {
        if (this.order == null) {
            this.order = new ArrayList<Order>();
        }
		return this.order;
	}

    public List<Association> getAssociations() {
        if (this.associations == null) {
            this.associations = new ArrayList<Association>();
        }
		return this.associations;
	}

	public static Query forClass(Class queryClass) {
		return new Query(queryClass);
	}
    
	public Query add(Expression criteria) {
        if (this.criteria == null) {
            this.criteria = new ArrayList<Expression>();
        }
		this.criteria.add(criteria);
		return this;
	}

    public Query ilike(String propertyName, String value) {
        add(SimpleExpression.ilike(propertyName, value));
        return this;
    }

    public Query eq(String propertyName, String value) {
        add(SimpleExpression.eq(propertyName, value));
        return this;
    }

    public Query eq(String propertyName, Long value) {
        add(SimpleExpression.eq(propertyName, value));
        return this;
    }

    public Query eq(String propertyName, Integer value) {
        add(SimpleExpression.eq(propertyName, value));
        return this;
    }

    public Query eq(String propertyName, Double value) {
        add(SimpleExpression.eq(propertyName, value));
        return this;
    }

    public Query eq(String propertyName, Date value) {
        add(SimpleExpression.eq(propertyName, value));
        return this;
    }

    public Query ge(String propertyName, String value) {
        add(SimpleExpression.ge(propertyName, value));
        return this;
    }

    public Query ge(String propertyName, Long value) {
        add(SimpleExpression.ge(propertyName, value));
        return this;
    }

    public Query ge(String propertyName, Integer value) {
        add(SimpleExpression.ge(propertyName, value));
        return this;
    }

    public Query ge(String propertyName, Double value) {
        add(SimpleExpression.ge(propertyName, value));
        return this;
    }

    public Query ge(String propertyName, Date value) {
        add(SimpleExpression.ge(propertyName, value));
        return this;
    }

    public Query gt(String propertyName, String value) {
        add(SimpleExpression.gt(propertyName, value));
        return this;
    }

    public Query le(String propertyName, String value) {
        add(SimpleExpression.le(propertyName, value));
        return this;
    }

    public Query like(String propertyName, String value) {
        add(SimpleExpression.like(propertyName, value));
        return this;
    }

    public Query lt(String propertyName, String value) {
        add(SimpleExpression.lt(propertyName, value));
        return this;
    }

    public Query asc(String propertyName) {
        if (this.order == null) {
            this.order = new ArrayList<Order>();
        }
        this.order.add(new Order(propertyName, true));
        return this;
    }

    public Query desc(String propertyName) {
        if (this.order == null) {
            this.order = new ArrayList<Order>();
        }
        this.order.add(new Order(propertyName, false));
        return this;
    }

    /**
     * Creates an inner join association between the forClass object
     * on the path entity with an alias. For example in Hibernate
     * the method would be used like<br />
     * Query.forClass(Person.class).innerJoin("role", "r")<br />
     *
     * @param path The path
     * @param alias The alias
     * @return Query
     */
    public Query innerJoin(String path, String alias) {
        if (this.associations == null) {
            this.associations = new ArrayList<Association>();
        }
        this.associations.add(new Association(path, alias, JoinType.INNER_JOIN));
        return this;
    }

    public Query fullJoin(String path, String alias) {
        if (this.associations == null) {
            this.associations = new ArrayList<Association>();
        }
        this.associations.add(new Association(path, alias, JoinType.FULL_JOIN));
        return this;
    }

    public Query leftOuterJoin(String path, String alias) {
        if (this.associations == null) {
            this.associations = new ArrayList<Association>();
        }
        this.associations.add(new Association(path, alias, JoinType.LEFT_OUTER_JOIN));
        return this;
    }

    public Query rightOuterJoin(String path, String alias) {
        if (this.associations == null) {
            this.associations = new ArrayList<Association>();
        }
        this.associations.add(new Association(path, alias, JoinType.RIGHT_OUTER_JOIN));
        return this;
    }
}
